package com.ionehe.group.common.file.minio.file;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.ionehe.group.common.file.FileServer;
import com.ionehe.group.common.file.configure.FileVendors;
import com.ionehe.group.common.file.configure.FileServerConfiguration;
import com.ionehe.group.common.file.enums.ContentTypeEnum;
import com.ionehe.group.common.file.exception.FileException;
import com.ionehe.group.common.file.minio.flags.MinioKeyFlags;
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Copyright (c) 2021 ionehe.com
 * Date: 2020/9/25
 * Time: 上午9:30
 *
 * @author 2020年 <a href="mailto:a@ionehe.com">秀</a>
 * 【minio客户端】
 */
@Slf4j
public class FileServerImpl implements FileServer {
    private final String endpoint;
    private final String bucket;
    private final MinioClient minioClient;


    public FileServerImpl(FileServerConfiguration conf) {
        log.info("Minio client init ...");
        Map<String, Object> vendor = (Map<String, Object>) conf.getVendors().get(FileVendors.minio);
        this.endpoint = MinioKeyFlags.FLAG_ENDPOINT.get(vendor);
        this.bucket = MinioKeyFlags.FLAG_BUCKET.get(vendor);
        String secret = MinioKeyFlags.FLAG_SECRET.get(vendor);
        String access = MinioKeyFlags.FLAG_ACCESS.get(vendor);

        try {
            // 创建客户端建立连接
            this.minioClient = MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(access, secret)
                    .build();
            log.info("Minio client init success!");
            // 创建bucket
            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
            if (!found) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
                log.info("Minio make bucket {} success!", bucket);
            }
        } catch (Exception e) {
            log.error("Minio client init error! endpoint:{}, bucket:{} cause:{}", endpoint, bucket,
                    Throwables.getStackTraceAsString(e));
            throw new FileException("initialize minio exception");
        }
    }


    @Override
    public String write(String fileName, MultipartFile file) throws FileException {
        log.info("FileServer[]write[]start! fileName:{}", fileName);
        if (StrUtil.isBlank(fileName) || Objects.isNull(file) || file.getSize() <= 0) {
            log.warn("FileServer[]write[]parameter illegal!");
            throw new FileException("parameter illegal");
        }

        InputStream inputStream = null;
        try {
            String ext = FileUtil.extName(fileName).toUpperCase();
            String contentType = ContentTypeEnum.valueOf(ext).getCentType();
            long start = System.currentTimeMillis();
            inputStream = file.getInputStream();
            ObjectWriteResponse objectWriteResponse = minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .contentType(contentType)
                            .object(fileName)
                            .stream(inputStream, file.getSize(), -1)
                            .build());

            if (Objects.isNull(objectWriteResponse)) {
                log.error("FileServer[]write[]error! fileName:{}, size:{}", fileName, file.getSize());
                return null;
            }
            long end = System.currentTimeMillis();
            log.info("耗时:{}毫秒", end - start);
            log.info("FileServer[]write[]success!");
            return getFileUrl(fileName);
        } catch (Exception e) {
            log.error("FileServer[]write[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return null;
        } finally {
            IoUtil.close(inputStream);
        }
    }


    @Override
    public String write(String fileName, File file) {
        log.info("FileServer[]write[]start! fileName:{}", fileName);
        if (StrUtil.isBlank(fileName) || FileUtil.size(file) <= 0) {
            log.warn("FileServer[]write[]parameter illegal!");
            throw new FileException("parameter illegal");
        }

        BufferedInputStream inputStream = null;
        try {
            String ext = FileUtil.extName(fileName).toUpperCase();
            String contentType = ContentTypeEnum.valueOf(ext).getCentType();
            log.info("FileServer[]write[]start! fileName:{}, contentType:{}", fileName, contentType);
            long start = System.currentTimeMillis();
            inputStream = FileUtil.getInputStream(file);
            ObjectWriteResponse objectWriteResponse = minioClient.putObject(
                    PutObjectArgs.builder().contentType(contentType)
                            .bucket(bucket)
                            .object(fileName)
                            .stream(inputStream, -1, 10485760)
                            .build());

            if (Objects.isNull(objectWriteResponse)) {
                log.error("FileServer[]write[]error! fileName{}", fileName);
                return null;
            }
            long end = System.currentTimeMillis();
            log.info("耗时:{}毫秒", end - start);
            log.info("FileServer[]write[]success!");
            return getFileUrl(fileName);
        } catch (Exception e) {
            log.error("FileServer[]write[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return null;
        } finally {
            IoUtil.close(inputStream);
        }
    }


    @Override
    public boolean delete(String fileName) {
        log.info("FileServer[]delete[]start! fileName:{}", fileName);
        if (StrUtil.isBlank(fileName)) {
            log.warn("FileServer[]write[]file is empty!");
            throw new FileException("file name can not be null");
        }

        return delete(Lists.newArrayList(fileName));
    }


    @Override
    public boolean delete(List<String> fileNameList) {
        log.info("FileServer[]delete[]start! fileNameList:{}", fileNameList);
        try {
            List<DeleteObject> objects = new LinkedList<>();
            fileNameList.forEach(fileName -> {
                String basePath = endpoint + "/" + bucket + "/";
                if (fileName.contains(basePath)) {
                    fileName = fileName.replace(basePath, "");
                }
                objects.add(new DeleteObject(fileName));
            });
            Iterable<Result<DeleteError>> results =
                    minioClient.removeObjects(
                            RemoveObjectsArgs.builder().bucket(bucket).objects(objects).build());
            if (Objects.isNull(results)) {
                log.error("FileServer[]delete[]error! fileNames:{}", fileNameList);
                return false;
            }

            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                log.warn("Error in deleting object " + error.objectName() + "; " + error.message());
            }
            log.info("FileServer[]delete[]success!");
            return true;
        } catch (Exception e) {
            log.info("FileServer[]delete[]error! fileNames:{}, cause:{}", fileNameList, Throwables.getStackTraceAsString(e));
            return false;
        }
    }


    @Override
    public InputStream getObject(String fileName) {
        log.info("FileServer[]getObject[]start! fileName:{}", fileName);
        String basePath = endpoint + "/" + bucket + "/";
        if (fileName.contains(basePath)) {
            fileName = fileName.replace(basePath, "");
        }
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucket)
                            .object(fileName)
                            .build());
        } catch (Exception e) {
            log.error("FileServer[]getObject[]bucketName:{}, fileName:{} error! cause:{}", bucket, fileName, Throwables.getStackTraceAsString(e));
            return null;
        }
    }


    @Override
    public String getFileUrl(String fileName) {
        return endpoint + "/" + bucket + "/" + fileName;
    }
}